package session;

import com.alibaba.fastjson2.JSONException;
import com.plealog.genericapp.api.EZEnvironment;
import util.StringUtil;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 利用反射简化session操作，其他session类都继承该类
 * <p>
 * BaseSession子类必须和BaseSession同级目录
 * 属性在更改时会触发监听器
 */
public class BaseSession {
	private static final Map<Class<? extends BaseSession>, PropertyChangeSupport> propertyChangeSupportMap = new HashMap<>();

	public static void setVal(Class<? extends BaseSession> c, String fieldName, Object newVal) {
		try {
			// 设置新值
			Field field = c.getField(fieldName);
			Object oldValue = field.get(null);
			field.set(null, newVal);

			// 触发监听器
			firePropertyChange(c, fieldName, oldValue, newVal);

			// 这里不再更新,只在程序结束时写入
			// if (field.getAnnotation(PersistentData.class) != null) {
			// 	// 本地更新
			// 	EZEnvironment.setApplicationProperty(fieldName, JSON.toJSONString(newVal));
			// }
		} catch (NoSuchFieldException | IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}

	public static Object getVal(Class<?> c, String fieldName) {
		try {
			// 设置新值
			Field field = c.getField(fieldName);
			return field.get(null);
		} catch (NoSuchFieldException | IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}

	public static void firePropertyChange(Class<? extends BaseSession> c, String propertyName, Object oldValue, Object newValue) {
		getPropertyChangeSupport(c).firePropertyChange(propertyName, oldValue, newValue);
	}

	/**
	 * 添加session数据更改的监听器，该监听器方法执行不保证在事件队列中执行。
	 * 要保证在事件队列中执行，有简便的实现类{@link javax.swing.event.SwingPropertyChangeSupport}
	 *
	 * @param c            改变数据的session
	 * @param propertyName 要更改值的属性名
	 * @param listener     值更改时调用的监听器
	 */
	public static void addPropertyChangeListener(Class<? extends BaseSession> c, String propertyName, PropertyChangeListener listener) {
		getPropertyChangeSupport(c).addPropertyChangeListener(propertyName, listener);
	}

	public static void removePropertyChangeListener(Class<? extends BaseSession> c, PropertyChangeListener listener) {
		getPropertyChangeSupport(c).removePropertyChangeListener(listener);
	}

	public static void removePropertyChangeListener(Class<? extends BaseSession> c, String propertyName, PropertyChangeListener listener) {
		getPropertyChangeSupport(c).removePropertyChangeListener(propertyName, listener);
	}

	public static boolean contains(Class<? extends BaseSession> c, String propertyName, PropertyChangeListener l) {
		return List.of(getPropertyChangeSupport(c).getPropertyChangeListeners(propertyName)).contains(l);
	}

	public static PropertyChangeSupport getPropertyChangeSupport(Class<? extends BaseSession> c) {
		PropertyChangeSupport pcs = propertyChangeSupportMap.get(c);
		if (pcs == null) {
			pcs = new PropertyChangeSupport(c);
			propertyChangeSupportMap.put(c, pcs);
		}
		return pcs;
	}

	public static ArrayList<Class<?>> SESSION_CLASS_LIST = new ArrayList<>();

	static {
		SESSION_CLASS_LIST.add(RoleSession.class);
	}

	/**
	 * 读取本地数据填充session字段
	 * <p>
	 * BaseSession子类必须和BaseSession同级目录
	 */
	public static void initSessionData() {

		// 读取该包下的class文件，由于打包成jar后getResource返回null，我不知道如何扫描，只能获取到流，故放弃扫描，直接给出
		// File dir = new File(BaseSession.class.getResource("/").getPath()
		//                     + BaseSession.class.getPackageName().replaceAll("\\.", "/"));
		//
		// Optional.ofNullable(dir.list((dir1, name) -> name.endsWith(".class")))
		// 		.ifPresent(strings -> Arrays.stream(strings)
		// 				.map(s -> s.substring(0, s.length() - ".class".length()))
		// 				.forEach(className -> {
		// 					if (className.equals(BaseSession.class.getSimpleName())) return;
		//
		// 					className = BaseSession.class.getPackageName() + "." + className;
		// 					try {
		//                       ...参照下面
		// 					} catch (Exception e) {
		// 						throw new RuntimeException(e);
		// 					}
		// 				})
		// 		);

		try {
			for (Class<?> aClass : SESSION_CLASS_LIST) {
				Field[] fields = aClass.getFields();
				for (Field f : fields) {
					if (f.getAnnotation(PersistentData.class) != null) {
						// 该字段需要从本地读取

						// 清空本地存储
						// EZEnvironment.setApplicationProperty(f.getName(), "");

						String val = EZEnvironment.getApplicationProperty(f.getName());
						if (!StringUtil.isEmpty(val)) {
							// TODO 这里使用递归更严谨一点儿
							try {
								// 调用READ_SESSION_DATA方法初始化session数据
								try {
									aClass.getMethod("READ_SESSION_DATA").invoke(null);
								} catch (NoSuchMethodException | SecurityException ignored) {
								}
							} catch (JSONException e) {
								System.err.println("解析失败，原因：" + e.getMessage());
								// 处理解析异常
								EZEnvironment.setApplicationProperty(f.getName(), "");
							}
						}
					}
				}
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 读取本地数据，不存在时返回空串
	 */
	public static String getLocalData(String key) {
		String s = EZEnvironment.getApplicationProperty(key);
		return s == null ? "" : s;
	}
}
